/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include "plugin_model.h"
#include "scripting.h"
#include "npruntime.h"
#include "plugin_impl.h"
#include "scripting_utils.h"

#define _TEST

#ifdef _TEST
#include <stdlib.h>
#include <stdio.h>
#endif


ScriptableImage::ScriptableImage( NPP npp )
    : NPObjectBase( npp ),
    m_item( NULL )
{
}

ScriptableImage::~ScriptableImage()
{
}

bool ScriptableImage::construct( const NPVariant *args, uint32_t argCount, NPVariant *result )
{
    NPObject *obj = ::NPN_CreateObject( m_npp, GET_NPOBJECT_CLASS( ScriptableImage ) );
    if ( !obj )
        return false;

    OBJECT_TO_NPVARIANT( obj, *result );

    return true;
}

bool ScriptableImage::hasMethod( NPIdentifier name )
{
    return name == npid.getImageData || name == npid.getThumbnailUrl;
}

bool ScriptableImage::hasProperty( NPIdentifier name )
{
    return name == npid.width || name == npid.height || name == npid.pixelType || name == npid.resolution || name == npid.index;
}

bool ScriptableImage::getProperty( NPIdentifier name, NPVariant *result )
{
    if ( m_item == NULL )
        return false;

    NPVariant& resultValue = *result;
    VOID_TO_NPVARIANT( resultValue );

    if ( name == npid.width )
    {
        INT32_TO_NPVARIANT( m_item->getWidth(), resultValue );
    }
    else if ( name == npid.height )
    {
        INT32_TO_NPVARIANT( m_item->getHeight(), resultValue );
    }
    else if ( name == npid.pixelType )
    {
        INT32_TO_NPVARIANT( ( int32_t ) m_item->getPixelType(), resultValue );
    }
    else if ( name == npid.resolution )
    {
        INT32_TO_NPVARIANT( m_item->getResolution(), resultValue );
    }
    else if ( name == npid.index )
    {
        BOOLEAN_TO_NPVARIANT( m_item->getIndex(), resultValue );
    }

    return true;
}

bool ScriptableImage::invoke( NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result )
{
    if ( m_item == NULL )
        return false;

    NPVariant& resultValue = *result;

    if ( name == npid.getPixel )
    {
        VOID_TO_NPVARIANT( resultValue );

        if ( argCount == 2 )
        {
            const NPVariant& vX = args[ 0 ], &vY = args[ 1 ];

            if ( IS_NUMERIC( vX ) && IS_NUMERIC( vY ) )
            {
                int x = AS_INT( vX );
                int y = AS_INT( vY );

                int pixel = m_item->getPixel( x, y );

                INT32_TO_NPVARIANT( pixel, resultValue );
            }
        }
    }
    else if ( name == npid.setPixel )
    {
        if ( argCount == 3 )
        {
            const NPVariant& vX = args[ 0 ], &vY = args[ 1 ], &vPixel = args[ 2 ];

            if ( IS_NUMERIC( vX ) && IS_NUMERIC( vY ) && IS_NUMERIC( vPixel ) )
            {
                int x = AS_INT( vX );
                int y = AS_INT( vY );
                int pixel = AS_INT( vPixel );

                m_item->setPixel( x, y, pixel );
            }
        }
    }
    else if ( name == npid.getImageData )
    {
        VOID_TO_NPVARIANT( resultValue );

        if ( argCount == 4 )
        {
            const NPVariant& vX = *args, &vY = args[ 1 ], &vWidth = args[ 2 ], &vHeight = args[ 3 ];

            if ( IS_NUMERIC( vX ) && IS_NUMERIC( vY ) && IS_NUMERIC( vWidth ) && IS_NUMERIC( vHeight ) )
            {
                int x = AS_INT( vX ), y = AS_INT( vY ), width = AS_INT( vWidth ), height = AS_INT( vHeight );

                if ( x >= 0 || y >= 0 || width > 0 || height > 0 )
                {
                    List<byte_t> list;

                    if ( m_item->getImageData( list, x, y, width, height ) )
                    {
                        //#ifdef _TEST
                        int t = ::GetTickCount();
                        //#endif

                        NPObject* npoArray = NULL;
                        NPVariant npvArray;

                        if ( !::NPN_Invoke( m_npp, m_pPlugin->m_npoWindow, npid.Array, NULL, 0, &npvArray ) )
                            return false;

                        npoArray = NPVARIANT_TO_OBJECT( npvArray );

                        NPVariant npvResult;

                        int count = list.count();

                        NPVariant* npva = new NPVariant[ count ];
                        int j = 0;

                        for( auto& i : list )
                        {
                            NPVariant& item = npva[ j++ ];
                            INT32_TO_NPVARIANT( ( int ) i, item );
                        }

                        NPVariant* npaEnd = npva + count;
                        int part = 32768;//16384;//8192;//4096;//2048;

                        for ( NPVariant* i = npva; i < npaEnd; i += part )
                        {
                            int tail = npaEnd - i;

                            if ( tail > 0 )
                            {
                                int n = tail < part ? tail : part;

                                if ( !::NPN_Invoke( m_npp, npoArray, npid.push, i, n, &npvResult ) )
                                    return false;
                            }
                        }

                        ::NPN_ReleaseVariantValue( &npvResult );

                        OBJECT_TO_NPVARIANT( npoArray, resultValue );

                        ::printf( "JsArray created: %d (ms)", ::GetTickCount() - t );

                        return true;
                    }
                }
            }
        }
    }
    else if ( name == npid.getThumbnailUrl )
    {
        VOID_TO_NPVARIANT( resultValue );

        const char* dataUrl = m_item->getThumbnailUrl();
        if ( dataUrl )
        {
            char* npDataUrl = ScriptingUtils::npstring( dataUrl );

            STRINGZ_TO_NPVARIANT( npDataUrl, resultValue );

            return true;
        }
    }

    return false;
}

void ScriptableImage::disposed()
{
    m_item = NULL;
}
